import { EditFormat, Mode, Model, ModelsData, RawModelInfo, TaskData } from '@common/types';
import React, { ReactNode, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { BsCodeSlash, BsFilter, BsLayoutSidebar } from 'react-icons/bs';
import { CgTerminal } from 'react-icons/cg';
import { GoProjectRoadmap } from 'react-icons/go';
import { IoMdClose } from 'react-icons/io';
import { VscLock, VscUnlock } from 'react-icons/vsc';
import { RiRobot2Line } from 'react-icons/ri';
import { useTranslation } from 'react-i18next';
import { getProviderModelId, AVAILABLE_PROVIDERS } from '@common/agent';

import { IconButton } from '@/components/common/IconButton';
import { ModelSelector, ModelSelectorRef } from '@/components/ModelSelector';
import { showErrorNotification } from '@/utils/notifications';
import { EditFormatSelector } from '@/components/PromptField/EditFormatSelector';
import { TaskWorkingMode } from '@/components/PromptField/TaskWorkingMode';
import { StyledTooltip } from '@/components/common/StyledTooltip';
import { useSettings } from '@/contexts/SettingsContext';
import { useProjectSettings } from '@/contexts/ProjectSettingsContext';
import { useApi } from '@/contexts/ApiContext';
import { useResponsive } from '@/hooks/useResponsive';
import { useModelProviders } from '@/contexts/ModelProviderContext';
import { useAgents } from '@/contexts/AgentsContext';

export type TaskBarRef = {
  openMainModelSelector: (model?: string) => void;
  openAgentModelSelector: (model?: string) => void;
};

type Props = {
  baseDir: string;
  task: TaskData;
  modelsData: ModelsData | null;
  mode: Mode;
  onModelsChange?: (modelsData: ModelsData | null) => void;
  runCommand: (command: string) => void;
  onToggleSidebar: () => void;
  updateTask: (updates: Partial<TaskData>, useOptimistic?: boolean) => void;
};

export const TaskBar = React.forwardRef<TaskBarRef, Props>(
  ({ baseDir, task, modelsData, mode, onModelsChange, runCommand, onToggleSidebar, updateTask }, ref) => {
    const { t } = useTranslation();
    const { settings, saveSettings } = useSettings();
    const { projectSettings } = useProjectSettings();
    const { models, providers } = useModelProviders();
    const api = useApi();
    const { isMobile } = useResponsive();
    const [isMerging, setIsMerging] = useState(false);

    const updatePreferredModels = useCallback(
      (model: string) => {
        if (!settings) {
          return;
        }
        const updatedSettings = {
          ...settings,
          preferredModels: [...new Set([model, ...settings.preferredModels])],
        };
        void saveSettings(updatedSettings);
      },
      [saveSettings, settings],
    );

    const aiderModels = useMemo(() => {
      const aiderModels: Model[] = [...models];
      aiderModels.sort((model, otherModel) => {
        const modelId = getProviderModelId(model);
        const otherModelId = getProviderModelId(otherModel);
        return modelId.localeCompare(otherModelId);
      });

      return aiderModels;
    }, [models]);
    const agentModelSelectorRef = useRef<ModelSelectorRef>(null);
    const mainModelSelectorRef = useRef<ModelSelectorRef>(null);
    const architectModelSelectorRef = useRef<ModelSelectorRef>(null);

    const { profiles: agentProfiles } = useAgents();
    const activeAgentProfile = useMemo(() => {
      return agentProfiles.find((profile) => profile.id === projectSettings?.agentProfileId);
    }, [projectSettings?.agentProfileId, agentProfiles]);

    const showAiderInfo = mode !== 'agent' || activeAgentProfile?.useAiderTools === true;

    // Use task provider/model first, fallback to active agent profile
    const effectiveAgentProvider = task.provider || activeAgentProfile?.provider;
    const effectiveAgentModel = task.model || activeAgentProfile?.model;
    const currentTaskModelId = effectiveAgentProvider && effectiveAgentModel ? `${effectiveAgentProvider}/${effectiveAgentModel}` : undefined;

    const taskModels = useMemo(() => {
      const taskModels: Model[] = [...models];

      // Add the currently selected model if it's not in the known list (custom model)
      if (currentTaskModelId) {
        const existingModel = taskModels.find((model) => getProviderModelId(model) === currentTaskModelId);
        if (!existingModel) {
          // Create a custom model object for the current model
          const [providerId, ...modelNameParts] = currentTaskModelId.split('/');
          const modelId = modelNameParts.join('/');
          if (providerId && modelId) {
            const customModel: Model = {
              id: modelId,
              providerId: providerId,
            };
            taskModels.unshift(customModel); // Add to the beginning for visibility
          }
        }
      }
      return taskModels;
    }, [currentTaskModelId, models]);

    const handleTaskModelChange = useCallback(
      (selectedModel: Model) => {
        const selectedModelId = getProviderModelId(selectedModel);
        const providerId = selectedModel.providerId;
        const modelId = selectedModel.id;

        if (!providerId || !modelId) {
          showErrorNotification(
            t('modelSelector.invalidModelSelection', {
              model: selectedModelId,
            }),
          );
          return;
        }

        const provider = providers.find((provider) => provider.id === providerId);
        if (!provider) {
          showErrorNotification(
            t('modelSelector.providerNotSupported', {
              provider: providerId,
              providers: AVAILABLE_PROVIDERS.join(', '),
            }),
          );
          return;
        }

        updateTask({
          provider: provider.id,
          model: modelId,
        });

        updatePreferredModels(selectedModelId);
      },
      [providers, updateTask, t, updatePreferredModels],
    );

    useImperativeHandle(ref, () => ({
      openMainModelSelector: (model) => {
        if (mode === 'architect') {
          architectModelSelectorRef.current?.open(model);
        } else {
          mainModelSelectorRef.current?.open(model);
        }
      },
      openAgentModelSelector: (model) => {
        agentModelSelectorRef.current?.open(model);
      },
    }));

    const renderModelInfo = useCallback(
      (modelName: string, info: RawModelInfo | undefined): ReactNode => {
        if (!info) {
          return <div className="text-xs text-text-primary">{modelName}</div>;
        }

        return (
          <div className="text-2xs text-text-secondary">
            <div className="flex items-center font-semibold text-xs text-text-primary mb-0.5">{modelName}</div>
            <div className="flex items-center">
              <span className="flex-1 mr-2">{t('modelInfo.maxInputTokens')}:</span> {info.max_input_tokens}
            </div>
            <div className="flex items-center">
              <span className="flex-1 mr-2">{t('modelInfo.maxOutputTokens')}:</span> {info.max_output_tokens}
            </div>
            <div className="flex items-center">
              <span className="flex-1 mr-2">{t('modelInfo.inputCostPerMillion')}:</span> ${((info.input_cost_per_token ?? 0) * 1_000_000).toFixed(2)}
            </div>
            <div className="flex items-center">
              <span className="flex-1 mr-2">{t('modelInfo.outputCostPerMillion')}:</span> ${((info.output_cost_per_token ?? 0) * 1_000_000).toFixed(2)}
            </div>
          </div>
        );
      },
      [t],
    );

    const updateEditFormat = useCallback(
      (format: EditFormat, modelToUpdate?: string) => {
        const targetModel = modelToUpdate || modelsData?.mainModel;
        if (!targetModel) {
          return;
        }

        api.updateEditFormats(baseDir, { [targetModel]: format });

        if (modelsData && onModelsChange) {
          // optimistic update
          onModelsChange({
            ...modelsData,
            editFormat: format,
          });
        }
      },
      [baseDir, modelsData, onModelsChange, api],
    );

    const updateMainModel = useCallback(
      (mainModel: Model) => {
        const modelId = getProviderModelId(mainModel);
        api.updateMainModel(baseDir, task.id, modelId);
        updatePreferredModels(modelId);

        if (modelsData && onModelsChange) {
          onModelsChange(null);
        }
      },
      [api, baseDir, task.id, modelsData, onModelsChange, updatePreferredModels],
    );

    const updateWeakModel = useCallback(
      (weakModel: Model) => {
        const modelId = getProviderModelId(weakModel);
        api.updateWeakModel(baseDir, task.id, modelId);
        updatePreferredModels(modelId);
        if (modelsData && onModelsChange) {
          onModelsChange({
            ...modelsData,
            weakModel: modelId,
          });
        }
      },
      [api, baseDir, task.id, updatePreferredModels, modelsData, onModelsChange],
    );

    const toggleWeakModelLock = useCallback(() => {
      updateTask({
        weakModelLocked: !task.weakModelLocked,
      });
    }, [task.weakModelLocked, updateTask]);

    const updateArchitectModel = useCallback(
      (architectModel: Model) => {
        const modelId = getProviderModelId(architectModel);
        api.updateArchitectModel(baseDir, task.id, modelId);
        updatePreferredModels(modelId);
        if (modelsData && onModelsChange) {
          onModelsChange({
            ...modelsData,
            architectModel: modelId,
          });
        }
      },
      [api, baseDir, task.id, modelsData, onModelsChange, updatePreferredModels],
    );

    const handleRemovePreferredModel = (model: string) => {
      if (!settings) {
        return;
      }
      const updatedSettings = {
        ...settings,
        preferredModels: settings.preferredModels.filter((preferred) => preferred !== model),
      };
      void saveSettings(updatedSettings);
    };

    const handleMerge = useCallback(
      async (squash: boolean) => {
        if (!task.worktree) {
          return;
        }

        setIsMerging(true);
        try {
          await api.mergeWorktreeToMain(baseDir, task.id, squash);
        } catch (error) {
          // eslint-disable-next-line no-console
          console.error('Failed to merge worktree:', error);
        } finally {
          setIsMerging(false);
        }
      },
      [api, baseDir, task.id, task.worktree],
    );

    const handleOnlyUncommitted = useCallback(async () => {
      if (!task.worktree) {
        return;
      }

      setIsMerging(true);
      try {
        await api.applyUncommittedChanges(baseDir, task.id);
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error('Failed to apply uncommitted changes:', error);
      } finally {
        setIsMerging(false);
      }
    }, [api, baseDir, task.id, task.worktree]);

    const handleRevert = useCallback(async () => {
      setIsMerging(true);
      try {
        await api.revertLastMerge(baseDir, task.id);
      } catch (error) {
        // eslint-disable-next-line no-console
        console.error('Failed to revert merge:', error);
      } finally {
        setIsMerging(false);
      }
    }, [api, baseDir, task.id]);

    const isTwoRowLayout = mode === 'agent' && showAiderInfo;
    const renderAiderInfo = (showLabel = false) => {
      return (
        <div className="flex flex-wrap items-center gap-x-2 gap-y-0.5">
          {showLabel && <span className="text-2xs font-semibold text-text-primary uppercase">{t('projectBar.aider')}:</span>}
          <div className="flex items-center space-x-1">
            <CgTerminal className="w-4 h-4 text-text-primary mr-1" data-tooltip-id="main-model-tooltip" />
            <StyledTooltip id="main-model-tooltip" content={renderModelInfo(t('modelSelector.mainModel'), modelsData?.info)} />
            <ModelSelector
              ref={mainModelSelectorRef}
              models={aiderModels}
              selectedModelId={modelsData?.mainModel}
              onChange={updateMainModel}
              preferredModelIds={settings?.preferredModels || []}
              removePreferredModel={handleRemovePreferredModel}
              providers={providers}
            />
          </div>
          <div className="h-3 w-px bg-bg-fourth"></div>
          <div className="flex items-center space-x-1">
            <BsFilter className="w-4 h-4 text-text-primary mr-1" data-tooltip-id="weak-model-tooltip" data-tooltip-content={t('modelSelector.weakModel')} />
            <StyledTooltip id="weak-model-tooltip" />
            <ModelSelector
              models={aiderModels}
              selectedModelId={modelsData?.weakModel || modelsData?.mainModel}
              onChange={updateWeakModel}
              preferredModelIds={settings?.preferredModels || []}
              removePreferredModel={handleRemovePreferredModel}
              providers={providers}
            />
            {task.weakModel && (
              <IconButton
                icon={task.weakModelLocked ? <VscLock className="w-4 h-4" /> : <VscUnlock className="w-4 h-4" />}
                onClick={toggleWeakModelLock}
                tooltip={task.weakModelLocked ? t('modelSelector.weakModelUnlock') : t('modelSelector.weakModelLock')}
              />
            )}
          </div>
          {modelsData?.editFormat && (
            <>
              <div className="h-3 w-px bg-bg-fourth"></div>
              <div className="flex items-center space-x-1">
                <BsCodeSlash className="w-4 h-4 text-text-primary mr-1" data-tooltip-id="edit-format-tooltip" />
                <StyledTooltip id="edit-format-tooltip" content={t('projectBar.editFormatTooltip')} />
                <EditFormatSelector currentFormat={modelsData.editFormat} onFormatChange={updateEditFormat} />
              </div>
            </>
          )}
          {modelsData?.reasoningEffort && modelsData.reasoningEffort !== 'none' && (
            <>
              <div className="h-3 w-px bg-bg-fourth"></div>
              <div className="flex items-center space-x-1 group/reasoning">
                <span className="text-xs text-text-muted-light">{t('modelSelector.reasoning')}:</span>
                <span className="text-text-primary text-xs">{modelsData.reasoningEffort}</span>
                <IconButton icon={<IoMdClose className="w-3 h-3" />} onClick={() => runCommand('reasoning-effort none')} className="ml-0.5" />
              </div>
            </>
          )}
          {modelsData?.thinkingTokens && modelsData?.thinkingTokens !== '0' && (
            <>
              <div className="h-3 w-px bg-bg-fourth"></div>
              <div className="flex items-center space-x-1 group/thinking">
                <span className="text-xs text-text-muted-light">{t('modelSelector.thinkingTokens')}:</span>
                <span className="text-text-primary text-xs">{modelsData.thinkingTokens}</span>
                <IconButton icon={<IoMdClose className="w-3 h-3" />} onClick={() => runCommand('think-tokens 0')} className="ml-0.5" />
              </div>
            </>
          )}
        </div>
      );
    };

    return (
      <div className="relative group px-4 py-2 pr-1 border-b border-border-dark-light bg-bg-primary-light min-h-10">
        <div className="flex items-center h-full">
          <div className="flex-grow">
            {isTwoRowLayout ? (
              // Two-row layout for Agent mode with Aider tools
              <div className="flex flex-wrap gap-x-5 gap-y-2">
                {/* Row 1: AGENT */}
                <div className="flex items-center space-x-2">
                  <div className="flex items-center space-x-1">
                    <RiRobot2Line className="w-4 h-4 text-text-primary mr-1" data-tooltip-id="agent-tooltip" />
                    <StyledTooltip id="agent-tooltip" content={t('modelSelector.agentModel')} />
                    {!currentTaskModelId ? (
                      <div className="text-xs text-text-muted-light">{t('modelSelector.noModelSelected')}</div>
                    ) : (
                      <ModelSelector
                        ref={agentModelSelectorRef}
                        models={taskModels}
                        selectedModelId={currentTaskModelId}
                        onChange={handleTaskModelChange}
                        preferredModelIds={settings?.preferredModels || []}
                        removePreferredModel={handleRemovePreferredModel}
                        providers={providers}
                      />
                    )}
                  </div>
                </div>

                {/* Row 2: AIDER */}
                {renderAiderInfo(true)}
              </div>
            ) : (
              // Original horizontal layout for other modes
              <div className="flex items-center space-x-3">
                {mode === 'agent' ? (
                  <>
                    <div className="flex items-center space-x-1">
                      <RiRobot2Line className="w-4 h-4 text-text-primary mr-1" data-tooltip-id="agent-tooltip" />
                      <StyledTooltip id="agent-tooltip" content={t('modelSelector.agentModel')} />
                      {!currentTaskModelId ? (
                        <div className="text-xs text-text-muted-light">{t('modelSelector.noModelSelected')}</div>
                      ) : (
                        <ModelSelector
                          ref={agentModelSelectorRef}
                          models={taskModels}
                          selectedModelId={currentTaskModelId}
                          onChange={handleTaskModelChange}
                          preferredModelIds={settings?.preferredModels || []}
                          removePreferredModel={handleRemovePreferredModel}
                          providers={providers}
                        />
                      )}
                    </div>
                    {showAiderInfo && <div className="h-3 w-px bg-bg-fourth"></div>}
                  </>
                ) : (
                  <>
                    {mode === 'architect' && (
                      <>
                        <div className="flex items-center space-x-1">
                          <GoProjectRoadmap
                            className="w-4 h-4 text-text-primary mr-1"
                            data-tooltip-id="architect-model-tooltip"
                            data-tooltip-content={t('modelSelector.architectModel')}
                          />
                          <StyledTooltip id="architect-model-tooltip" />
                          <ModelSelector
                            ref={architectModelSelectorRef}
                            models={aiderModels}
                            selectedModelId={modelsData?.architectModel || modelsData?.mainModel}
                            onChange={updateArchitectModel}
                            preferredModelIds={settings?.preferredModels || []}
                            removePreferredModel={handleRemovePreferredModel}
                            providers={providers}
                          />
                        </div>
                        <div className="h-3 w-px bg-bg-fourth"></div>
                      </>
                    )}
                  </>
                )}
                {showAiderInfo && renderAiderInfo()}
              </div>
            )}
          </div>
          <div className="flex items-center space-x-1 mr-2">
            <TaskWorkingMode
              task={task}
              onMerge={() => handleMerge(false)}
              onSquash={() => handleMerge(true)}
              onOnlyUncommitted={handleOnlyUncommitted}
              onRevert={handleRevert}
              isMerging={isMerging}
            />
            {isMobile && (
              <IconButton
                icon={<BsLayoutSidebar className="w-4 h-4" />}
                onClick={onToggleSidebar}
                tooltip={t('projectBar.toggleSidebar')}
                className="p-1 hover:bg-bg-tertiary rounded-md"
              />
            )}
          </div>
        </div>
      </div>
    );
  },
);

TaskBar.displayName = 'ProjectTopBar';
